//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2002-2007
// (c) Philipp Peshin (phil@mobilae.ru), 2003
//
// 'Create' tool.
//

# include "StdAfx.h"
# include "Resource.h"
# include "ToolCreate.h"
# include "Tool.h"
# include "CreateObject.h"
# include "Position.h"
# include "Scale.h"
# include "Levels.h"
# include "MapView.h"
# include "Selection.h"
# include "ChangeType.h"
# include "Rename.h"
# include "RgnProperties.h"
# include "DrawHelpers.h"
# include "Menu.h"
# include "Load.h"
# include "Options.h"
# include "Globals.h"

class CCreateTool : public CTool {
  public:
	CCreateTool () : CTool (toolCreate) {}

	virtual bool Init ();
	virtual void Destroy ();

	virtual void OnActivate ();
	virtual void OnRemoveMap ();

	virtual void OnLDown (int _x, int _y, DWORD _dwKeys);
	virtual void OnLUp   (int _x, int _y, DWORD _dwKeys);
	virtual void OnMove  (int _x, int _y, DWORD _dwKeys);
	virtual void OnRUp   (int _x, int _y, DWORD _dwKeys);

	virtual void OnPaint (HDC _hDC, const RECT & _rect);

	virtual void OnCtxMenu (int _x, int _y);
	virtual void OnEsc ();
	virtual void OnBack ();
	virtual void OnEnter ();
	virtual bool OnCommand (WORD _wID);
	virtual void OnSwitchSubTool ();

  public:
	points_t m_points;
	point_t m_pointLast;
	POINT m_ptLast;
	bool m_bStick;
	bool m_bAfterLDown;

	HMENU m_hmenuCreate;

	HPEN m_hpenStick;
	HPEN m_hpenNoStick;

	HCURSOR m_hcurCreatePoint;
	HCURSOR m_hcurCreatePolyline;
	HCURSOR m_hcurCreatePolygon;

	void _SetCreateCursor ();
};

DECLARE_TOOL (CCreateTool);

bool CCreateTool::Init () {
	m_bStick      = false;
	m_bAfterLDown = false;

	m_hcurCreatePoint    = ::LoadCursor (g_hInst, MAKEINTRESOURCE (IDC_CursorCreatePoint));
	m_hcurCreatePolyline = ::LoadCursor (g_hInst, MAKEINTRESOURCE (IDC_CursorCreatePolyline));
	m_hcurCreatePolygon  = ::LoadCursor (g_hInst, MAKEINTRESOURCE (IDC_CursorCreatePolygon));

	m_hpenStick          = ::CreatePen (PS_SOLID, 1, c_colCyan);
	m_hpenNoStick        = ::CreatePen (PS_SOLID, 1, c_colGray);

	m_hmenuCreate = ::GetSubMenu (LoadMenu (IDM_POPUP), smpCreate);
	assert (m_hmenuCreate);

	return true;
}

void CCreateTool::Destroy () {
	if (m_hpenStick)
		::DeleteObject (m_hpenStick);
	if (m_hpenNoStick)
		::DeleteObject (m_hpenNoStick);
}

void CCreateTool::_SetCreateCursor () {
	switch (GetCreateKind ()) {
		case rkPoint:    CTool::SetPermanentCursor (m_hcurCreatePoint);    break;
		case rkPolyline: CTool::SetPermanentCursor (m_hcurCreatePolyline); break;
		case rkPolygon:  CTool::SetPermanentCursor (m_hcurCreatePolygon);  break;
	}
}

void CCreateTool::OnSwitchSubTool () {
	switch (GetCreateKind ()) {
		case rkPoint:    SetCreateKind (rkPolyline); break;
		case rkPolyline: SetCreateKind (rkPolygon);  break;
		case rkPolygon:  SetCreateKind (rkPoint);    break;
	}

	_SetCreateCursor ();
}

void CCreateTool::OnActivate () {
	_SetCreateCursor ();
	m_points.clear ();

	m_bStick = false;
}

void CCreateTool::OnRemoveMap () {
	m_points.clear ();

	m_bStick = false;
}

void CCreateTool::OnLDown (int _x, int _y, DWORD _dwKeys) {
	m_bAfterLDown = true;
}

void CCreateTool::OnLUp (int _x, int _y, DWORD _dwKeys) {
	if (! m_bAfterLDown)
		return;
	m_bAfterLDown = false;

	_SetCreateCursor ();

	const size_t cLevel = GetZoomLevel (g_fScale);
	if (cLevel != -1 && cLevel < g_map.Levels.size ())
		// NOTE: this may change _x & _y.
		m_bStick = StickPointToNodeOrGrid (cLevel, _x, _y, m_pointLast);
	else {
		ScreenToPt (_x, _y, m_pointLast);
		m_bStick = false;
	}
	m_ptLast.x = _x;
	m_ptLast.y = _y;

	const RGN_KIND Kind = GetCreateKind ();
	if (Kind == rkPoint) {
		g_Selection.clear ();

		points_t points (1);
		points [0] = m_pointLast;

		CreateObject (points, Kind, GetZoomLevel (g_fScale));
		Refresh ();

		// Show 'Change Type' dialog.
		ShowChangeTypeForSelection (g_hWnd, Kind);
		// Edit label.
		OnEditLabelStart ();
	} else {
		assert (Kind == rkPolyline || Kind == rkPolygon);

		if (m_points.empty () || ! (m_points.back () == m_pointLast))
			m_points.push_back (m_pointLast);

		Redraw ();
	}
}

void CCreateTool::OnMove (int _x, int _y, DWORD _dwKeys) {
	_SetCreateCursor ();

	const int iPrevLastX = m_ptLast.x;
	const int iPrevLastY = m_ptLast.y;

	const size_t cLevel = GetZoomLevel (g_fScale);
	if (cLevel != -1 && cLevel < g_map.Levels.size ())
		// NOTE: this may change _x & _y.
		m_bStick = StickPointToNodeOrGrid (cLevel, _x, _y, m_pointLast);
	else {
		ScreenToPt (_x, _y, m_pointLast);
		m_bStick = false;
	}
	m_ptLast.x = _x;
	m_ptLast.y = _y;

	if (! m_points.empty ())
		Redraw ();
	else if (iPrevLastX != m_ptLast.x || iPrevLastY != m_ptLast.y) {
		// Redraw stick mark only.
		RECT rect;

		if (iPrevLastX < m_ptLast.x) {
			rect.left = iPrevLastX;
			rect.right = m_ptLast.x;
		} else {
			rect.right = iPrevLastX;
			rect.left = m_ptLast.x;
		}

		if (iPrevLastY < m_ptLast.y) {
			rect.top = iPrevLastY;
			rect.bottom = m_ptLast.y;
		} else {
			rect.bottom = iPrevLastY;
			rect.top = m_ptLast.y;
		}
		::InflateRect (& rect, c_iCrossSize + 1, c_iCrossSize + 1);

		Redraw (& rect);
	}
}

void CCreateTool::OnPaint (HDC _hDC, const RECT & _rect) {
	//
	// Draw cross for stick point, if any.
	//

	const int x = m_ptLast.x;//XToScreen (m_pointLast.x);
	const int y = m_ptLast.y;//YToScreen (m_pointLast.y);

	if (m_bStick) 
		::SelectObject (_hDC, m_hpenStick);
    else
	    ::SelectObject (_hDC, m_hpenNoStick);
	::SetROP2 (_hDC, R2_XORPEN);

	DrawCross (_hDC, x, y);

	//
	// Draw polyline of object to create.
	//

	if (m_points.empty ())
		return;

	const size_t cPoints = m_points.size ();
	POINT * const pPoints = reinterpret_cast<POINT *> (::alloca (sizeof (POINT)*(cPoints + 1)));
	for (size_t cPoint = 0; cPoint < cPoints; ++ cPoint) {
		const point_t & point = m_points [cPoint];
		pPoints [cPoint].x = XToScreen (point.x);
		pPoints [cPoint].y = YToScreen (point.y);
	}
	pPoints [cPoints].x = x;
	pPoints [cPoints].y = y;

	const HPEN hpenPolyline = ::CreatePen (PS_SOLID, GetNewObjectWidth (), GetNewObjectColor ());
	const HPEN hpen = (HPEN) ::SelectObject (_hDC, hpenPolyline);

	::SetROP2 (_hDC, R2_COPYPEN);

	::Polyline (_hDC, pPoints, cPoints + 1);

	// Complete polygon, if any.
	if (GetCreateKind () == rkPolygon) {
		::MoveToEx (_hDC, pPoints [cPoints].x, pPoints [cPoints].y, NULL);
		::LineTo   (_hDC, pPoints [0].x,       pPoints [0].y);
	}

	::SelectObject (_hDC, hpen);
	::DeleteObject (hpenPolyline);
}

void CCreateTool::OnRUp (int _x, int _y, DWORD _dwKeys) {
	CCreateTool::OnCtxMenu (_x, _y);
}

void CCreateTool::OnEsc () {
	POINT p;
	::GetCursorPos (& p);
	::ScreenToClient (g_hMapWnd, & p);
	CCreateTool::OnCtxMenu (p.x, p.y);
}

void CCreateTool::OnCtxMenu (int _x, int _y) {
	if (g_bLoading)
		return;

	const RGN_KIND Kind = GetCreateKind ();
	if (Kind == rkPoint)
		return;

	const size_t cPoints = m_points.size ();
	const bool bCanEnd = Kind == rkPolyline ? cPoints >= 2 : cPoints >= 3;

	::EnableMenuItem (m_hmenuCreate, ID_CREATE_REMOVE_LAST_NODE, (cPoints > 0 ? MF_ENABLED : MF_GRAYED) | MF_BYCOMMAND);
	::EnableMenuItem (m_hmenuCreate, ID_CREATE_END,              (bCanEnd     ? MF_ENABLED : MF_GRAYED) | MF_BYCOMMAND);
	::EnableMenuItem (m_hmenuCreate, ID_CREATE_CONTINUE,         (cPoints > 0 ? MF_ENABLED : MF_GRAYED) | MF_BYCOMMAND);
//	::EnableMenuItem (m_hmenuCreate, ID_CREATE_CANCEL,           (cPoints > 0 ? MF_ENABLED : MF_GRAYED) | MF_BYCOMMAND);

	PopupMenu (m_hmenuCreate);
}

void CCreateTool::OnBack () {
	CCreateTool::OnCommand (ID_CREATE_REMOVE_LAST_NODE);
}

void CCreateTool::OnEnter () {
	CCreateTool::OnCommand (ID_CREATE_END);
}

bool CCreateTool::OnCommand (WORD _wID) {
	switch (_wID) {
		case ID_CREATE_REMOVE_LAST_NODE:
			if (! m_points.empty ())
				m_points.erase (m_points.end () - 1);
			Redraw ();
			return true;

		case ID_CREATE_END: {
			const RGN_KIND Kind = GetCreateKind ();
			if (
				(Kind == rkPolyline && m_points.size () < 2) ||
				(Kind == rkPolygon  && m_points.size () < 3)
			)
				return true;

			if (Kind == rkPolyline || Kind == rkPolygon) {
				g_Selection.clear ();

				// Complete the object creation.
				points_t points;
				points.swap (m_points);
				CreateObject (points, Kind, GetZoomLevel (g_fScale));
				Refresh ();

				assert (m_points.empty ());

				// Show 'Change Type' dialog.
				ShowChangeTypeForSelection (g_hWnd, Kind);
				// Edit label.
				OnEditLabelStart ();
			}
			return true;
		}

		case ID_CREATE_CONTINUE:
			return true;

		case ID_CREATE_CANCEL:
			m_points.clear ();
			Redraw ();
			return true;
	}

	return false;
}

////////////////////////////////////////

static RGN_KIND g_Kind = rkPolyline;

void SetCreateKind (RGN_KIND _Kind) {
	if (g_Kind == _Kind)
		return;
	g_Kind = _Kind;

	CTool * const pToolCreate = CTool::GetCurrentTool ();
	if (pToolCreate && pToolCreate->m_ToolId == toolCreate) {
		pToolCreate->OnDeactivate ();
		pToolCreate->OnActivate ();
	}
	Redraw ();
}

RGN_KIND GetCreateKind () {return g_Kind;}
